#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <libiptc/libiptc.h>
#include <arpa/inet.h> 

static int append_rule (const char *table, const char *chain, unsigned int src, int inverted_src, unsigned int dest, int inverted_dst, const char *target)
{
  struct{
      struct ipt_entry entry;
      struct xt_standard_target target;
    } entry;
  struct xtc_handle *h;
  int ret = 1;
 
  h = iptc_init (table);
  if (!h)
  {
    fprintf (stderr, "Could not init IPTC library: %s\n", iptc_strerror (errno));
    goto out;
  }
 
  memset (&entry, 0, sizeof (entry));
 
  /* target */
  entry.target.target.u.user.target_size = XT_ALIGN (sizeof (struct xt_standard_target));
  strncpy (entry.target.target.u.user.name, target, sizeof (entry.target.target.u.user.name));
 
  /* entry */
  entry.entry.target_offset = sizeof (struct ipt_entry);
  entry.entry.next_offset = entry.entry.target_offset + entry.target.target.u.user.target_size;
  
  if (src)
  {
    entry.entry.ip.src.s_addr  = src;
    entry.entry.ip.smsk.s_addr = 0xFFFFFFFF;
    if (inverted_src)
      entry.entry.ip.invflags |= IPT_INV_SRCIP;
  }
 
  if (dest)
  {
    entry.entry.ip.dst.s_addr  = dest;
    entry.entry.ip.dmsk.s_addr = 0xFFFFFFFF;
    if (inverted_dst)
      entry.entry.ip.invflags |= IPT_INV_DSTIP;
  }
  
  if (!iptc_append_entry (chain, (struct ipt_entry *) &entry, h))
  {
    fprintf (stderr, "Could not insert a rule in iptables (table %s): %s\n", table, iptc_strerror (errno));
    goto out;
  }
 
  if (!iptc_commit (h))
  {
    fprintf (stderr, "Could not commit changes in iptables (table %s): %s\n", table, iptc_strerror (errno));
    goto out;
  }
 
  ret = 0;
out:
  if (h)
    iptc_free (h);
 
  return ret;
}
 
static int insert_rule (const char *table, const char *chain, unsigned int src, int inverted_src, unsigned int dest, int inverted_dst, const char *target)
{
  struct{
      struct ipt_entry entry;
      struct xt_standard_target target;
    } entry;
  struct xtc_handle *h;
  int ret = 0;
 
  h = iptc_init (table);
  if (!h)
  {
    fprintf (stderr, "Could not init IPTC library: %s\n", iptc_strerror (errno));
    goto out;
  }
 
  memset (&entry, 0, sizeof (entry));
 
  /* target */
  entry.target.target.u.user.target_size = XT_ALIGN (sizeof (struct xt_standard_target));
  strncpy (entry.target.target.u.user.name, target, sizeof (entry.target.target.u.user.name));
 
  /* entry */
  entry.entry.target_offset = sizeof (struct ipt_entry);
  entry.entry.next_offset = entry.entry.target_offset + entry.target.target.u.user.target_size;
  
  if (src)
  {
    entry.entry.ip.src.s_addr  = src;
    entry.entry.ip.smsk.s_addr = 0xFFFFFFFF;
    if (inverted_src)
      entry.entry.ip.invflags |= IPT_INV_SRCIP;
  }
 
  if (dest)
  {
    entry.entry.ip.dst.s_addr  = dest;
    entry.entry.ip.dmsk.s_addr = 0xFFFFFFFF;
    if (inverted_dst)
      entry.entry.ip.invflags |= IPT_INV_DSTIP;
  }
  
  if (!iptc_insert_entry(chain, (struct ipt_entry *) &entry,(unsigned int)0, h))
  {
    fprintf (stderr, "Could not insert a rule in iptables (table %s): %s\n", table, iptc_strerror (errno));
    goto out;
  }
 
  if (!iptc_commit (h))
  {
    fprintf (stderr, "Could not commit changes in iptables (table %s): %s\n", table, iptc_strerror (errno));
    goto out;
  }
 
  ret = 1;
out:
  if (h)
    iptc_free (h);
 
  return ret;
}

static int delete_rule (char *table, char *chain, unsigned int src, int inverted_src, unsigned int dest, int inverted_dst, const char *target)
{
  struct{
      struct ipt_entry entry;
      struct xt_standard_target target;
    } entry;
  struct xtc_handle *h;
  int ret = 0;

  h = iptc_init (table);
  if (!h)
  {
    fprintf (stderr, "Could not init IPTC library: %s\n", iptc_strerror (errno));
    goto out;
  }

  memset (&entry, 0, sizeof (entry));

  /* target */
  entry.target.target.u.user.target_size = XT_ALIGN (sizeof (struct xt_standard_target));
  strncpy (entry.target.target.u.user.name, target, sizeof (entry.target.target.u.user.name));

  /* entry */
  entry.entry.target_offset = sizeof (struct ipt_entry);
  entry.entry.next_offset = entry.entry.target_offset + entry.target.target.u.user.target_size;

  if (src)
  {
    entry.entry.ip.src.s_addr  = src;
    entry.entry.ip.smsk.s_addr = 0xFFFFFFFF;
    if (inverted_src)
      entry.entry.ip.invflags |= IPT_INV_SRCIP;
  }

  if (dest)
  {
    entry.entry.ip.dst.s_addr  = dest;
    entry.entry.ip.dmsk.s_addr = 0xFFFFFFFF;
    if (inverted_dst)
      entry.entry.ip.invflags |= IPT_INV_DSTIP;
  }

  struct in_addr myaddr;
  char srcadd[20];
  memcpy(&myaddr, &src, 4);
  strcpy(srcadd, inet_ntoa(myaddr));
  int rulenum = search_num_of_ip_in_chain(chain, h, srcadd);
  if(rulenum == -1)
    return 0;
 
  if (!iptc_delete_num_entry(chain, rulenum, h))
  {
    fprintf (stderr, "Could not delete a rule in iptables (table %s): %s\n", table, iptc_strerror (errno));
    goto out;
  }

  if (!iptc_commit (h))
  {
    fprintf (stderr, "Could not commit changes in iptables (table %s): %s\n", table, iptc_strerror (errno));
    goto out;
  }

  ret = 1;
out:
  if (h)
    iptc_free (h);

  return ret;
}


int search_num_of_ip_in_chain(char* chain, struct xtc_handle *handle, char destadr[])
{
  struct ipt_entry *rule;
  int rulenum = 0;  
  const char* adr = destadr;
  rule = iptc_first_rule(chain, handle);
 // printf("dest: %s\n", dest);
  while(rule)
  {
//    printf("src: %s, dest: %s\n", inet_ntoa(rule->ip.src), destadr);
    if(strcmp(inet_ntoa(rule->ip.src), adr) == 0)
    {
      return rulenum;
    }
    rulenum ++;
    rule = iptc_next_rule(rule, handle);
  }
  
  return -1;
}

void trace_rules_in_chain(char* chain, struct xtc_handle *handle)
{
  struct ipt_entry *rule;
	
  rule = iptc_first_rule(chain, handle);
  while(rule)
  {
    printf("%d\n", rule->ip.src);
    rule = iptc_next_rule(rule, handle);	
  }
}

int deny_user(char* ip)
{
  unsigned int uint_ip;
  struct ipt_entry *rule;
  struct xtc_handle *h;
  int num;

#ifdef _DEBUG
  printf("deny user coming\n");
#endif

  h = iptc_init("filter");
  if((num = search_num_of_ip_in_chain("FORWARD", h, ip)) != -1)
  {
    return ; 
  }  

#ifdef _DEBUG
  printf("num = %d\n", num);
#endif 
 
  inet_pton (AF_INET, ip, &uint_ip);
  delete_rule("nat", "PREROUTING", uint_ip, 0, NULL, 0, "ACCEPT");
  return insert_rule("filter", "FORWARD", uint_ip, 0, NULL, 0, "DROP"); 
}

int allow_user(char* ip)
{
  unsigned int uint_ip;
  inet_pton (AF_INET, ip, &uint_ip);
  insert_rule("nat", "PREROUTING", uint_ip, 0, NULL, 0, "ACCEPT");
  return delete_rule("filter", "FORWARD", uint_ip, 0, NULL, 0, "DROP");
}

/*
int main (int argc, char **argv)
{
  unsigned int a, b;
 /*iptables -t filter -A INPUT -s 1.2.3.4/32 ! -d 4.3.2.1/32 -j DROP
 // inet_pton (AF_INET, "192.168.111.52", &a);
  //inet_pton (AF_INET, "4.3.2.1", &b);
 
  //insert_rule ("filter", "FORWARD", a, 0, NULL, 0, "DROP");
//  struct xtc_handle *h;
//  h = iptc_init ("nat");
  
  //printf("%d\n", search_num_of_ip_in_chain("PREROUTING", h, "192.168.111.52"));
//  trace_rules_in_chain("PREROUTING",h); 
//  printf("%d\n", allow_user("192.168.111.51"));
//  if(h)
 //   iptc_free(h);

  deny_user("192.168.111.52");
//  allow_user("192.168.111.52");
  return 0;
}*/
